
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Globalization;
using System.Text;

namespace Swaf
{
	/// <summary>
	/// This class will take multiple IComparable implementations and compare them
	/// to an object passed in. It will do it in the order in which they were added.
	/// </summary>
    [Serializable]
	public class CompositeComparable : IComparable
	{
        protected bool m_hasConflictingEncoding = false;
		protected ArrayList m_comparables = new ArrayList();
        protected bool m_toStringComparable = false;

		public CompositeComparable()
		{
		}

        public CompositeComparable(bool hasConflictingEncoding)
        {
            m_hasConflictingEncoding = hasConflictingEncoding;
        }

        public CompositeComparable(bool hasConflictingEncoding, bool toStringComparable)
        {
            m_hasConflictingEncoding = hasConflictingEncoding;
            m_toStringComparable = toStringComparable;
        }

        public bool HasConflictingEncoding
        {
            get { return m_hasConflictingEncoding; }
            set { m_hasConflictingEncoding = value; }
        }

		#region IComparable Members

		public int CompareTo(object obj)
		{
            // Because of legacy code, if the obj is a String, compare against the toString version
            // of this composite comparable
            if (obj is string)
            {
                return ToString().CompareTo(obj);
            }

			CompositeComparable inboundComparable = obj as CompositeComparable;
			if (inboundComparable == null)
			{
				throw new BaseException(string.Format("Invalid compare argument {0}, must be of type CompositeComparable", obj));
			}

            // if toStringComparable is set then we'll convert in bound composite comparable to string and compare
            if (m_toStringComparable)
            {
                string thisStr = ToString();
                string thatStr = inboundComparable.ToString();
                int cmpRes = thisStr.CompareTo(thatStr);
                return cmpRes;
            }

            // Two Composite Comparables, so compare as "normal"

			int compareResult = 0;

			for (int i = 0; i < inboundComparable.m_comparables.Count; ++i)
			{
				// Compare obj against this instance of the IComparable

				IComparable compareToVal = inboundComparable.m_comparables[i] as IComparable;
				IComparable thisVal = m_comparables[i] as IComparable;

				// Handle possible nulls
				if ((compareToVal == null) && (thisVal == null))
				{
					compareResult = 0;

					// Continue to the next element
					continue;
				}

				// Comparing object is null
				if (compareToVal == null)
				{
					// thisVal's non-null value is greater than compareToVal's null value
					compareResult = 1;

					// Found a mis-match so we can break out of loop
					break;
				}

				// This obj is null
				if (thisVal == null)
				{
					// thisVal's null value is less than compareToVal's non-null value
					compareResult = -1;

					// Found a mis-match so we can break out of loop
					break;
				}

                if (HasConflictingEncoding && (thisVal is string) && (compareToVal is string))
                {
                    // Use a culture compare CompareOptions.StringSort parameters if the encoding doesn't
                    // match the encoding of the system
                    compareResult = CultureInfo.CurrentCulture.CompareInfo.Compare(thisVal as string, compareToVal as string, CompareOptions.StringSort);
                }
                else
                {
                    compareResult = thisVal.CompareTo(compareToVal);
                }
				if (compareResult != 0)
				{
					// if the results is < or > than 0 then we know that we
					// don't have a match so it can be returned
					break;
				}
			}

			return compareResult;
		}

		#endregion

		/// <summary>
		/// Addes a comparable to the composite comparable
		/// </summary>
		/// <param name="comparable"></param>
		public void Add(object comparable)
		{
			if (comparable == null || comparable is IComparable)
			{
				m_comparables.Add(comparable);
			}
			else
			{
				throw new BaseException(string.Format("Invalid type to include in Composite Comparable, null be an IComparable.  Type passed was: {0}", comparable.GetType().Name));
			}
		}

		/// <summary>
		/// Compare two CompositeComparables
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public override bool Equals(object obj)
		{
			if (obj == null)
			{
				return false;
			}
			if (!(obj is CompositeComparable) && !(obj is string))
			{
				throw new BaseException(string.Format("Invalid object type for comparison, type must be CompositComparable, type passed was {0}", (obj != null) ? obj.GetType().Name : string.Empty));
			}

			if (obj is CompositeComparable)
				return CompareTo(obj) == 0;
			else
				return ToString().CompareTo(obj) == 0;
		}

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

		/// <summary>
		/// Override default ToString impl to concat all comparables together to produce "old style" values.
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			StringBuilder toStr = new StringBuilder(m_comparables.Count);
			for (int i = 0; i < m_comparables.Count; ++i)
			{
                object comparable = m_comparables[i];
				toStr.Append((comparable != null) ? comparable.ToString() : string.Empty);
			}
		
			return toStr.ToString();
		}
	}
}
